#include "common.h"

#ifdef USE_NVS
#include "nonVolatileStorage.h"

#ifdef SDCC
#include <malloc.h>
#endif

#ifdef __STC12C5410AD_H__
#define BANK_START 0x2800
#define BANK_COUNT 4
#define BANK_SIZE 512
#define BANK_SIZE_TOTAL BANK_COUNT*BANK_SIZE
//if STC12C5412 is used, the bank can be whole program space
//user can define his own bank space def
#endif

#define ISP_CONTR_ENABLE		(1<<7)
#define ISP_CONTR_SWBS		(1<<6)
#define ISP_CONTR_SWRST		(1<<5)
#define ISP_CONTR_CMD_FAIL	(1<<4)
#define ISP_CONTR_WAIT		(0x01 | (1<<3))	//24MHz wait time
#define ISP_TRIG_CMD1			0x46;
#define ISP_TRIG_CMD2			0xB9;

static volatile NVS_MEM_SEGMENT *nvsSegPointer;
static volatile uint nvsSegAddr;
#define NVS_GET_ADDR_HIGH(i) ((i>>8)&0xff)
#define NVS_GET_ADDR_LOW(i) (i&0xff)

bool getCurrentSegAddr(uint *addr) {
	uint i,start,end;
	uchar dat;
	//use dichotomy search to address the current memory segment
	for(i=start=0,end=BANK_SIZE_TOTAL;
		end-start>NVS_MEM_SEGMENT_SIZE;
		) {
		i=(end-start)/NVS_MEM_SEGMENT_SIZE/2*NVS_MEM_SEGMENT_SIZE+start;
		if(!NVS_OPT_BYTE(&dat, i+BANK_START, NVS_READ)) {
			return false;
		}
		if(dat==NVS_MEM_SEGMENT_FLAG) {
			start=i;
		}
		else {
			end=i;
		}
	}
	*addr=start+BANK_START;
	return true;
}

bool NVS_INIT() {
//allocate mem
#if USE_HEAP
	nvsSegPointer=(NVS_MEM_SEGMENT*)malloc(sizeof(NVS_MEM_SEGMENT));
#else
	static volatile NVS_MEM_SEGMENT xdata _nvsSegPointer;
	nvsSegPointer=&_nvsSegPointer;
#endif

//get current segment addr
	if(!getCurrentSegAddr(&nvsSegAddr)) {
		nvsSegAddr=0;
		return false;
	}

	return true;
}

bool NVS_LOAD() {
	uchar i,dat;
	uchar* pt;
	if(!NVS_ISVALID())
		return false;
	//read from eeprom
	pt=nvsSegPointer->raw;
	for(i=0;i<NVS_MEM_SEGMENT_SIZE;i++) {
		if(!NVS_OPT_BYTE(&dat, nvsSegAddr+i, NVS_READ)) {
			nvsSegAddr=0;
			return false;
		}
		if(i==0 && dat!=NVS_MEM_SEGMENT_FLAG) {
			return false;
		}
		*pt++=dat;
	}
	return true;
}

bool NVS_STORE() {
	uchar i,dat;
	uchar* pt;
	if(!NVS_ISVALID())
		return false;
	//update nvsSegAddr. if nvs is full, erase whole nvs space
	if(nvsSegAddr+NVS_MEM_SEGMENT_SIZE>=BANK_START+BANK_SIZE_TOTAL) {
		NVS_CLEAR();
	}
	else {
		nvsSegAddr+=NVS_MEM_SEGMENT_SIZE;
	}
	//update flag
	nvsSegPointer->flag=NVS_MEM_SEGMENT_FLAG;
	//write to eeprom
	pt=nvsSegPointer->raw;
	for(i=0;i<NVS_MEM_SEGMENT_SIZE;i++) {
		dat=*pt++;
		if(!NVS_OPT_BYTE(&dat, nvsSegAddr+i, NVS_WRITE)) {
			nvsSegAddr=0;
			return false;
		}
	}
	return true;
}

bool NVS_ISVALID() {
	if(nvsSegAddr) {
		return true;
	}
	return false;
}

NVS_MEM_SEGMENT* NVS_GET() {
	return nvsSegPointer;
}

bool NVS_CLEAR() {
	uchar i,dat;
	for(i=0;i<BANK_COUNT;i++) {
		if(!NVS_OPT_BYTE(&dat,BANK_START+i*BANK_SIZE,NVS_ERASE)) {
			nvsSegAddr=0;
			return false;
		}
	}
	nvsSegAddr=BANK_START;
	return true;
}
////////////////////////////////////////////////
//	DEBUG FUNCTIONS
uint NVS_ADDR() {
	return nvsSegAddr;
}

#define NVS_CHK_OPT

#ifdef NVS_CHK_OPT
bool NVS_CHECK_ADDR(uint addr, NvsOpt opt) {
	if(opt==NVS_ERASE) {
		uchar bankId=0;
		for(bankId;bankId<BANK_COUNT;bankId++) {
			if(addr==BANK_START+BANK_SIZE*bankId) {
				return true;
			}
		}
		return false;
	}
	else {
		if(addr<BANK_START || addr>=BANK_START+BANK_SIZE_TOTAL)
			return false;
	}
	return true;
}
#endif

bool NVS_OPT_BYTE(uchar *dat, uint addr, NvsOpt opt) {
	if(opt==NVS_STANDBY) {
		return true;
	}
#ifdef NVS_CHK_OPT
	if(!NVS_CHECK_ADDR(addr,opt))
		return false;
#endif
	if(opt==NVS_WRITE) {
		ISP_DATA=*dat;
	}
	ISP_ADDRH=NVS_GET_ADDR_HIGH(addr);
	ISP_ADDRL=NVS_GET_ADDR_LOW(addr);
	ISP_CONTR=ISP_CONTR_ENABLE | ISP_CONTR_WAIT;
	ISP_CMD=opt;
	ISP_TRIG=ISP_TRIG_CMD1;
	ISP_TRIG=ISP_TRIG_CMD2;
	if(ISP_CONTR&ISP_CONTR_CMD_FAIL) {
		ISP_CONTR=0;
		return false;
	}
	if(opt==NVS_READ) {
		*dat=ISP_DATA;
	}
	ISP_CONTR=0;
	return true;
}
#endif

